package com.code.leetcode._202508;

import java.util.HashMap;
import java.util.Map;

//65 有效数字
public class IsNumber {
    /**
     * 给你一个字符串s，返回是否是一个有效数字
     * 例如：下面的都是有效数字："2","0089","-0.1","+3.14","4.","-.9","2e10","-90E3",
     * "3e+7", "+6e-1", "53.5e93", "-123.456e789"，
     * 而接下来的不是："abc", "1a", "1e", "e3", "99e2.5", "--6", "-+3", "95a54e53"
     * 一般的，一个有效数字可以用以下的规则之一定义：
     * 1、一个整数后面跟着一个可选指数
     * 2、一个十进制后面跟着一个可选指数
     * 一个整数定义为一个可选符号'-'或'+'后面跟着数字
     * 一个十进制定义为一个可选符号'-'或'+'后面跟着下述规则：
     * 1、数字后跟着一个小数点。
     * 2、数字后面跟着一个小数点.再跟着数位
     * 3、一个小数点.后面跟着数位
     * 指数定义为指数付哈'e'或'E'，后面跟着一个整数
     * 数字定义为一个或多个数位
     * 示例1：输入：s = "0"
     * 输出：true
     * 示例2：输入：s = "e"
     * 输出：false
     * 示例3：输入：s = "."
     * 输出：false
     **/
    public static void main(String[] args) {
        IsNumber i = new IsNumber();
        System.out.println(i.isNumbers("0"));
    }

    /**
     * 确认有限状态自动机，是一类计算模型，它包含一系列状态，这些状态中
     * 1、有一中特殊的状态，被称为初始状态
     * 2、还有一系列状态被称为接受状态，他们组成了一个特殊的集合。其中，一个状态可能既是初始状态，也是接受状态
     * 起初，这个自动机处于初始状态。随后，它顺序地读取字符串中的每一个字符，并根据当前状态和读入的字符，按照某个
     * 事先约定好的转移规则，从当前状态转移到下一个状态，当状态转移完成后，它就读取下一个字符，当字符串全部读取完毕
     * 后，如果自动机处于某个接受状态，则判定该字符串被接受，否则，判定该字符串被拒绝。
     * 注意：如果输入的过程中某一步转移失败了，即不存在对应的转移规则，此时计算将提前中止，在这种情况下我们也判定该字符串被拒绝
     * 一个自动机，总能够回答某种形式的对于给定的输入字符串s，判断其是否满足条件p的问题。在本题中，条件p即为构成
     * 合法的表示数值的字符串
     * 自动机驱动的编程，可以被看做一种暴力枚举方式的延申，它穷尽了在任何一种情况下，对于任何的输入，需要做的事情。
     * 根据上面的描述，现在可以定义自动机的住状态集合了，那么怎么挖掘出所哟一可能的状态呢？一个常用的技巧是，用当前处理到的字符串
     * 的哪个部分当作状态的表述，根据这一技巧，不难挖掘出所有状态：
     * 0、初始状态
     * 1、符号位
     * 2、整数部分
     * 3、左侧有整数的小数点
     * 4、左侧无整数的小数点
     * 5、小数部分
     * 6、字符e
     * 7、指数部分的符号位
     * 8、指数部分的整数部分
     * 下一步是找到初始状态和接受状态的集合，根据题意，初始状态应当为状态0，而接受状态的集合则为状态2，
     * 状态3，状态5以及状态8。换言之，字符串的末尾要么是空格，要么是数字，要么是小数点，但前提是小数点的前面有数字
     * 最后，需要定义 转移规则，结合数值字符串应当具备的格式
     * 我们没有单独地考虑到每种字符，而是划分为若干类，由于全部10数组字符批次之间都等价，因此只需定义一种统一地数字类型
     * 即可，对于正负号也是同理
     **/
    public boolean isNumbers(String s) {
        Map<State, Map<CharType, State>> transfer = new HashMap<>();
        Map<CharType, State> initialMap = new HashMap<>() {{
            put(CharType.CHAR_NUMBER, State.STATE_INTEGER);
            put(CharType.CHAR_POINT, State.STATE_POINT_WITHOUT_INT);
            put(CharType.CHAR_SIGN, State.STATE_EXP_SIGN);
        }};
        transfer.put(State.STATE_INITIAL,initialMap);
        Map<CharType,State> intSignMap  = new HashMap<>(){{
            put(CharType.CHAR_NUMBER, State.STATE_INTEGER);
            put(CharType.CHAR_POINT, State.STATE_POINT_WITHOUT_INT);
        }};
        transfer.put(State.STATE_INT_SIGN, intSignMap);
        Map<CharType, State> integerMap = new HashMap<>() {{
            put(CharType.CHAR_NUMBER, State.STATE_INTEGER);
            put(CharType.CHAR_EXP, State.STATE_EXP);
            put(CharType.CHAR_POINT, State.STATE_POINT);
        }};
        transfer.put(State.STATE_INTEGER, integerMap);
        Map<CharType, State> pointMap = new HashMap<>() {{
            put(CharType.CHAR_NUMBER, State.STATE_FRACTION);
            put(CharType.CHAR_EXP, State.STATE_EXP);
        }};
        transfer.put(State.STATE_POINT, pointMap);
        Map<CharType, State> pointWithoutIntMap = new HashMap<>() {{
            put(CharType.CHAR_NUMBER, State.STATE_FRACTION);
        }};
        transfer.put(State.STATE_POINT_WITHOUT_INT, pointWithoutIntMap);
        Map<CharType, State> fractionMap = new HashMap<>() {{
            put(CharType.CHAR_NUMBER, State.STATE_FRACTION);
            put(CharType.CHAR_EXP, State.STATE_EXP);
        }};
        transfer.put(State.STATE_FRACTION, fractionMap);
        Map<CharType, State> expMap = new HashMap<>() {{
            put(CharType.CHAR_NUMBER, State.STATE_EXP_NUMBER);
            put(CharType.CHAR_SIGN, State.STATE_EXP_SIGN);
        }};
        transfer.put(State.STATE_EXP, expMap);
        Map<CharType, State> expSignMap = new HashMap<>() {{
            put(CharType.CHAR_NUMBER, State.STATE_EXP_NUMBER);
        }};
        transfer.put(State.STATE_EXP_SIGN, expSignMap);
        Map<CharType, State> expNumberMap = new HashMap<>() {{
            put(CharType.CHAR_NUMBER, State.STATE_EXP_NUMBER);
        }};
        transfer.put(State.STATE_EXP_NUMBER, expNumberMap);

        int length = s.length();
        State state = State.STATE_INITIAL;
        for (int i = 0; i < length; i++) {
            CharType type = toCharType(s.charAt(i));
            if (!transfer.get(state).containsKey(type)){
                return false;
            }else {
                state = transfer.get(state).get(type);
            }
        }
        return state == State.STATE_INTEGER || state == State.STATE_POINT
                || state == State.STATE_FRACTION || state == State.STATE_EXP_NUMBER
                || state == State.STATE_END;
    }

    public CharType toCharType(char ch) {
        if (ch >= '0' && ch <= '9') {
            return CharType.CHAR_NUMBER;
        } else if (ch == 'e' || ch == 'E') {
            return CharType.CHAR_EXP;
        } else if (ch == '.') {
            return CharType.CHAR_POINT;
        } else if (ch == '+' || ch == '-') {
            return CharType.CHAR_SIGN;
        } else {
            return CharType.CHAR_ILLEGAL;
        }
    }

    enum State {
        STATE_INITIAL,//初始状态
        STATE_INT_SIGN,//符号位
        STATE_INTEGER,//整数部分
        STATE_POINT,//小数点 有整数
        STATE_POINT_WITHOUT_INT,//小数点无整数
        STATE_FRACTION,//小数部分
        STATE_EXP,//字符e
        STATE_EXP_SIGN,//指数符号
        STATE_EXP_NUMBER,//指数数字
        STATE_END
    }

    enum CharType {
        CHAR_NUMBER,
        CHAR_EXP,
        CHAR_POINT,
        CHAR_SIGN,
        CHAR_ILLEGAL
    }
}
